import socket
import threading
import time
import signal  # 信号处理模块

class ChatServer:
    def __init__(self, host='0.0.0.0', port=8888):
        self.host = host
        self.port = port
        self.server_socket = None
        self.clients = []  # 存储所有客户端连接 (socket, username)
        self.clients_lock = threading.Lock()
        self.running = False  # 服务器运行状态标志
        self.server_thread = None  # 服务器主线程

    def signal_handler(self, signum, frame):
        """处理Ctrl+C信号，优雅退出"""
        print("\nReceived Ctrl+C, shutting down server...")
        self.running = False  # 设置退出标志

    def start(self):
        # 注册信号处理函数
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        
        self.running = True
        # 创建TCP socket并设置为非阻塞模式（避免accept阻塞导致无法响应信号）
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.setblocking(False)  # 非阻塞模式
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(5)
        print(f"Chat server started on {self.host}:{self.port}")
        print("Press Ctrl+C to stop server")

        # 启动服务器主循环线程
        self.server_thread = threading.Thread(target=self.accept_connections)
        self.server_thread.start()
        
        # 等待服务器线程结束
        self.server_thread.join()
        self.stop()

    def accept_connections(self):
        """接受客户端连接的主循环（非阻塞）"""
        while self.running:
            try:
                # 非阻塞模式下，无连接时会抛出BlockingIOError
                client_socket, client_addr = self.server_socket.accept()
                print(f"New connection from {client_addr}")
                
                # 为每个客户端创建处理线程
                client_thread = threading.Thread(
                    target=self.handle_client,
                    args=(client_socket, client_addr),
                    daemon=True  # 守护线程，主程序退出时自动终止
                )
                client_thread.start()
            except BlockingIOError:
                # 无新连接时短暂休眠，减少CPU占用
                time.sleep(0.1)
            except Exception as e:
                if self.running:  # 仅在服务器运行时打印错误
                    print(f"Error accepting connection: {e}")
                time.sleep(0.1)

    def handle_client(self, client_socket, client_addr):
        """处理单个客户端的消息交互"""
        username = None
        try:
            # 设置客户端socket超时（避免无限阻塞）
            client_socket.settimeout(10.0)  # 10秒超时
            
            # 发送请求用户名消息
            welcome_msg = "Please enter your username: ".encode('utf-8')
            client_socket.sendall(welcome_msg)
            print(f"Sent username request to {client_addr}")

            # 接收用户名（最多等待10秒）
            username_data = client_socket.recv(1024).decode('utf-8', errors='ignore').strip()
            if not username_data:
                raise Exception("No username received")
            
            username = username_data[:20]  # 限制用户名长度
            print(f"Received username '{username}' from {client_addr}")

            # 将客户端加入列表
            with self.clients_lock:
                self.clients.append((client_socket, username))
            
            # 广播用户加入消息
            join_msg = f"[System] {username} has joined the chat!\n".encode('utf-8')
            self.broadcast(join_msg, client_socket)
            
            # 发送欢迎消息
            client_socket.sendall(f"Welcome {username}! Type 'exit' to leave.\n".encode('utf-8'))

            # 消息接收循环（设置超时，便于检测服务器是否退出）
            client_socket.settimeout(1.0)  # 缩短超时，便于响应服务器退出
            while self.running:
                try:
                    data = client_socket.recv(1024)
                except socket.timeout:
                    continue  # 超时继续循环，检查服务器是否仍在运行
                    
                if not data:
                    raise Exception("Connection closed by client")
                
                message = data.decode('utf-8', errors='ignore').strip()
                print(f"Received from {username}: {message}")

                # 处理退出命令
                if message.lower() == 'exit':
                    client_socket.sendall("Goodbye!\n".encode('utf-8'))
                    break
                
                # 广播消息
                full_msg = f"[{username}]: {message}\n".encode('utf-8')
                self.broadcast(full_msg, client_socket)

        except socket.timeout:
            print(f"Timeout with {client_addr} (no activity for 10s)")
        except Exception as e:
            print(f"Error handling {client_addr}: {e}")
        finally:
            # 清理客户端连接
            client_socket.close()
            print(f"Closed socket for {client_addr}")
            
            # 从客户端列表移除
            with self.clients_lock:
                self.clients = [(sock, name) for sock, name in self.clients if sock != client_socket]
            
            # 广播用户离开消息（如果已获取用户名）
            if username:
                leave_msg = f"[System] {username} has left the chat.\n".encode('utf-8')
                self.broadcast(leave_msg, None)
            
            print(f"Connection with {client_addr} ({username or 'unknown'}) fully closed")

    def broadcast(self, message, exclude_socket):
        """广播消息给所有客户端（排除发送者）"""
        with self.clients_lock:
            # 复制一份客户端列表，避免遍历中修改导致异常
            clients_copy = list(self.clients)
        
        for client_socket, _ in clients_copy:
            if client_socket != exclude_socket:
                try:
                    client_socket.sendall(message)
                except:
                    # 发送失败，可能客户端已断开，后续会被清理
                    pass

    def stop(self):
        """停止服务器并清理资源"""
        self.running = False
        print("Stopping server...")
        
        # 关闭服务器socket
        if self.server_socket:
            try:
                self.server_socket.close()
            except Exception as e:
                print(f"Error closing server socket: {e}")
        
        # 关闭所有客户端连接
        with self.clients_lock:
            for client_socket, username in self.clients:
                try:
                    client_socket.sendall("Server is shutting down.\n".encode('utf-8'))
                    client_socket.close()
                    print(f"Closed connection with {username}")
                except:
                    pass
            self.clients.clear()
        
        print("Server stopped successfully")

if __name__ == "__main__":
    server = ChatServer(port=8888)
    server.start()